package com.bstek.uflo.designer.deserializer.impl;

import java.util.ArrayList;
import java.util.List;

import org.dom4j.Element;
import org.springframework.util.StringUtils;

import com.bstek.uflo.designer.deserializer.ShapeDeserializer;
import com.bstek.uflo.designer.model.ComponentAuthority;
import com.bstek.uflo.designer.model.ConditionType;
import com.bstek.uflo.designer.model.FormElement;
import com.bstek.uflo.designer.model.Mapping;
import com.bstek.uflo.designer.model.Point;
import com.bstek.uflo.designer.model.Rectangle;
import com.bstek.uflo.designer.model.Shape;
import com.bstek.uflo.designer.model.edge.Connection;
import com.bstek.uflo.designer.model.node.Node;
import com.bstek.uflo.designer.model.node.RectNode;
import com.bstek.uflo.designer.model.node.Start;
import com.bstek.uflo.designer.model.node.Task;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
public abstract class NodeDeserializer implements ShapeDeserializer {

	public abstract Shape execute(Element currentElement) throws Exception;

	public Shape deserializeCommonAttribute(Node node, Element currentElement) throws Exception {
		String name = currentElement.attributeValue("name");
		String eventHandlerBean = currentElement.attributeValue("event-handler-bean");
		node.setName(name);
		node.setEventHandlerBean(eventHandlerBean);
		Element descriptionElement = currentElement.element("description");
		if (descriptionElement != null) {
			String desc = descriptionElement.getText();
			node.setDescription(desc);
		}
		this.deserializeLoactionAttribute(node, currentElement);
		this.deserializeSequenceFlowAttribute(node, currentElement);
		this.deserializeComponentAttribute(node, currentElement);
		this.deserializeFormAttribute(node, currentElement);
		return node;
	}

	private void deserializeLoactionAttribute(Node node, Element currentElement) {
		String g = currentElement.attributeValue("g");
		String[] location = g.split(",");
		int x = Integer.parseInt(location[0]);
		int y = Integer.parseInt(location[1]);
		int width = Integer.parseInt(location[2]);
		int height = Integer.parseInt(location[3]);
		x = x + 50;
		y = y + 25;
		height = height - 30;
		if (node instanceof RectNode) {
			height = height + 15;
			if (height < 40) {
				height = 40;
			}
			x = x + 10;
		} else {
			if (height != 30) {
				height = 30;
			}
			if (width != 30) {
				width = 30;
			}
			y = y + 5;
			x = x - 30;
		}
		node.setRectangle(new Rectangle(new Point(x, y), width, height));
	}

	private void deserializeSequenceFlowAttribute(Node node, Element currentElement) {
		List<?> sequenceFlowElements = currentElement.elements("sequence-flow");
		for (Object sequenceFlowObj : sequenceFlowElements) {
			Element sequenceFlowElement = (Element) sequenceFlowObj;
			Connection conn = new Connection();
			String name = sequenceFlowElement.attributeValue("name");
			String toNodeName = sequenceFlowElement.attributeValue("to");
			String g = sequenceFlowElement.attributeValue("g");
			String expression = sequenceFlowElement.attributeValue("expression");
			String handlerBean = sequenceFlowElement.attributeValue("handler-bean");

			conn.setName(name);
			conn.setToNodeName(toNodeName);
			conn.setFromNodeId(node.getId());
			conn.setFromNodeName(node.getName());
			conn.setVertices(buildConnectionVertices(g));
			conn.setExpression(expression);
			conn.setHandlerBean(handlerBean);
			if (StringUtils.hasText(expression)) {
				conn.setConditionType(ConditionType.Expression.name());
			} else if (StringUtils.hasText(handlerBean)) {
				conn.setConditionType(ConditionType.Handler.name());
			} else {
				conn.setConditionType(ConditionType.None.name());
			}
			node.getOutConnections().add(conn);
		}
	}

	private void deserializeComponentAttribute(Node node, Element currentElement) {
		List<?> componentAuthorityElements = currentElement.elements("component-authority");
		if (componentAuthorityElements != null) {
			for (Object componentAuthorityObj : componentAuthorityElements) {
				Element componentAuthorityElement = (Element) componentAuthorityObj;
				ComponentAuthority componentAuthority = new ComponentAuthority();
				componentAuthority.setAuthority(componentAuthorityElement.attributeValue("authority"));
				componentAuthority.setComponent(componentAuthorityElement.attributeValue("component"));
				if (node instanceof Task) {
					((Task) node).getComponentAuthorities().add(componentAuthority);
				} else if (node instanceof Start) {
					((Start) node).getComponentAuthorities().add(componentAuthority);
				}
			}
		}

	}

	private void deserializeFormAttribute(Node node, Element currentElement) {
		String formTemplate = currentElement.attributeValue("form-template");
		Start start = null;
		Task task = null;
		if (StringUtils.hasText(formTemplate)) {
			if (node instanceof Start) {
				start = ((Start) node);
				start.setFormTemplate(formTemplate);
			} else if (node instanceof Task) {
				task = ((Task) node);
				task.setFormTemplate(formTemplate);
			}
		}
		List<?> formElementElements = currentElement.elements("form-element");
		for (Object formElementObj : formElementElements) {
			Element formElementElement = (Element) formElementObj;
			FormElement formElement = new FormElement();
			String name = formElementElement.attributeValue("name");
			String caption = formElementElement.attributeValue("caption");
			String dataType = formElementElement.attributeValue("data-type");
			String editorType = formElementElement.attributeValue("editor-type");
			String authority = formElementElement.attributeValue("authority");
			String required = formElementElement.attributeValue("required");
			String defaultValue = formElementElement.attributeValue("default-value");
			formElement.setName(name);
			formElement.setCaption(caption);
			formElement.setDataType(dataType);
			formElement.setEditorType(editorType);
			formElement.setAuthority(authority);
			formElement.setRequired(Boolean.valueOf(required));
			formElement.setDefaultValue(defaultValue);
			List<?> mappingElements = formElementElement.elements("mapping");
			for (Object mappingObj : mappingElements) {
				Element mappingElement = (Element) mappingObj;
				String key = mappingElement.attributeValue("key");
				String label = mappingElement.attributeValue("label");
				Mapping mapping = new Mapping(key, label);
				formElement.getMappings().add(mapping);
			}
			if (start != null) {
				start.getFormElements().add(formElement);
			} else if (task != null) {
				task.getFormElements().add(formElement);
			}
		}

	}

	private List<Point> buildConnectionVertices(String g) {
		List<Point> points = new ArrayList<Point>();
		if (g != null && g.indexOf(":") != -1) {
			String v = g.substring(0, g.indexOf(":"));
			String[] gValues = v.split(";");
			for (String s : gValues) {
				String[] p = s.split(",");
				if (StringUtils.hasText(p[0]) && StringUtils.hasText(p[1])) {
					int x = Integer.parseInt(p[0]);
					int y = Integer.parseInt(p[1]);
					points.add(new Point(x, y));
				}
			}
		}
		return points;
	}

	public boolean support(String elementName) {
		return false;
	}

}
